feat: add migration for blast radius analysis (CM-1328)#4374
Conversation
PR SummaryLow Risk Overview
The change is additive only ( Reviewed by Cursor Bugbot for commit fb94246. Bugbot is set up for automated code reviews on this repo. Configure here. |
There was a problem hiding this comment.
Pull request overview
Adds PostgreSQL persistence for the four-stage blast-radius analysis pipeline.
Changes:
- Adds schemas for analyses, symbol specifications, dependents, verdicts, and stage runs.
- Adds integrity constraints and query indexes.
- Keeps aggregate reports derived from verdict data.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
b2dd456 to
99915ef
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 1 out of 1 changed files in this pull request and generated 4 comments.
Comments suppressed due to low confidence (1)
backend/src/osspckgs/migrations/V1784700000__blast_radius_analyses.sql:97
- These two foreign keys are independent, so the database accepts a verdict whose
analysis_idis analysis A whiledependent_idbelongs to analysis B. Queries aggregating directly byblast_radius_verdicts.analysis_idwould then report cross-analysis data. Enforce the pair with a composite foreign key to the dependent's(id, analysis_id), or remove the redundant analysis ID and derive it through the dependent.
analysis_id UUID NOT NULL REFERENCES blast_radius_analyses (id),
dependent_id BIGINT NOT NULL UNIQUE REFERENCES blast_radius_dependents (id),
99915ef to
fbb843b
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 1 out of 1 changed files in this pull request and generated 1 comment.
Comments suppressed due to low confidence (4)
backend/src/osspckgs/migrations/V1784700000__blast_radius_analyses.sql:97
- These two foreign keys are independent, so the database accepts a verdict whose
analysis_idbelongs to analysis A whiledependent_idbelongs to analysis B. Because reports aggregate verdicts directly byanalysis_id, such a row would silently contaminate the report. Enforce the pair with a composite foreign key (and a matching unique key on dependents), or remove the duplicatedanalysis_idand derive it through the dependent.
analysis_id UUID NOT NULL REFERENCES blast_radius_analyses (id),
dependent_id BIGINT NOT NULL UNIQUE REFERENCES blast_radius_dependents (id),
backend/src/osspckgs/migrations/V1784700000__blast_radius_analyses.sql:89
- This index duplicates the leftmost prefix of the unique index created by
UNIQUE (analysis_id, name). PostgreSQL can use that unique index foranalysis_idlookups, so this adds write and storage overhead without enabling another access path.
CREATE INDEX IF NOT EXISTS blast_radius_dependents_analysis_id_idx
ON blast_radius_dependents (analysis_id);
backend/src/osspckgs/migrations/V1784700000__blast_radius_analyses.sql:113
- The following
(analysis_id, reachable_verdict)index already supports queries filtering only by its leftmostanalysis_idcolumn. Keeping this single-column index duplicates that access path and adds unnecessary write/storage cost.
CREATE INDEX IF NOT EXISTS blast_radius_verdicts_analysis_id_idx
ON blast_radius_verdicts (analysis_id);
backend/src/osspckgs/migrations/V1784700000__blast_radius_analyses.sql:135
UNIQUE (analysis_id, stage)already creates a B-tree whose leftmost prefix servesanalysis_idlookups. This additional index is redundant and increases write amplification and storage.
CREATE INDEX IF NOT EXISTS blast_radius_stage_runs_analysis_id_idx
ON blast_radius_stage_runs (analysis_id);
Signed-off-by: Umberto Sgueglia <usgueglia@contractor.linuxfoundation.org>
fbb843b to
fb94246
Compare
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit fb94246. Configure here.
| CREATE TABLE IF NOT EXISTS blast_radius_verdicts ( | ||
| id BIGSERIAL PRIMARY KEY, | ||
| analysis_id UUID NOT NULL REFERENCES blast_radius_analyses (id), | ||
| dependent_id BIGINT NOT NULL UNIQUE REFERENCES blast_radius_dependents (id), |
There was a problem hiding this comment.
Verdicts allowed for excluded dependents
Medium Severity
The migration documents reachability verdicts as one row per non-excluded dependent, yet blast_radius_verdicts only references blast_radius_dependents by id. Rows with excluded_by_range = true can still get a verdict, which would incorrectly affect true blast-radius totals derived from verdicts.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit fb94246. Configure here.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 1 out of 1 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (4)
backend/src/osspckgs/migrations/V1784700000__blast_radius_analyses.sql:97
analysis_idanddependent_idare enforced independently, so the database accepts a verdict whose dependent belongs to a different analysis. Any report filtered byblast_radius_verdicts.analysis_idcan then attribute that verdict to the wrong job. Make the pair relationally consistent, for example with a composite foreign key to(analysis_id, id)on dependents, or remove the duplicatedanalysis_idand derive it through the dependent.
analysis_id UUID NOT NULL REFERENCES blast_radius_analyses (id),
dependent_id BIGINT NOT NULL UNIQUE REFERENCES blast_radius_dependents (id),
backend/src/osspckgs/migrations/V1784700000__blast_radius_analyses.sql:89
- This index duplicates the leading-column lookup already provided by
UNIQUE (analysis_id, name)on line 85. PostgreSQL can use that unique index foranalysis_id = ...; retaining both adds storage and write amplification without adding a query path.
CREATE INDEX IF NOT EXISTS blast_radius_dependents_analysis_id_idx
ON blast_radius_dependents (analysis_id);
backend/src/osspckgs/migrations/V1784700000__blast_radius_analyses.sql:113
- The following
(analysis_id, reachable_verdict)index already supports lookups on its leadinganalysis_idcolumn, making this standalone index redundant. Removing it avoids maintaining two indexes for the same access path.
CREATE INDEX IF NOT EXISTS blast_radius_verdicts_analysis_id_idx
ON blast_radius_verdicts (analysis_id);
backend/src/osspckgs/migrations/V1784700000__blast_radius_analyses.sql:135
UNIQUE (analysis_id, stage)on line 131 already creates an index usable foranalysis_idlookups. This additional index is redundant and adds unnecessary storage and write overhead.
CREATE INDEX IF NOT EXISTS blast_radius_stage_runs_analysis_id_idx
ON blast_radius_stage_runs (analysis_id);


Summary
Adds the database schema for the blast-radius analysis pipeline (CM-1328). This PR is migrations only — it lays the ground for replicating the blast-radius PoC pipeline (intel → dependents → reachability → report) on top of the new blast-radius worker, backed by the database instead of the PoC's per-run JSON files under
data/<VULN-ID>/. API/data-access-layer wiring lands in a follow-up PR.Changes
backend/src/osspckgs/migrations/V1784700000__blast_radius_analyses.sql, adding 5 tables:blast_radius_analyses— one row per submitted job (idmatches theanalysisIdalready generated bysubmitBlastRadiusJob). Stores the rawadvisory_osv_id/package_namefrom the request plus nullableadvisory_id/package_idFKs, resolved later — the advisory or package may not be ingested yet when the job is submitted.blast_radius_symbol_specs— stage 1 (intel) output, 1:1 with an analysis.blast_radius_dependents— stage 2 output; unifies the PoC's "analyzed" and "excluded_by_range" dependent lists into one table via anexcluded_by_rangeflag, since excluded candidates never get a resolved version/tarball.blast_radius_verdicts— stage 3 output, 1:1 with a non-excluded dependent, including per-verdictcost_usd/turns_used/model.blast_radius_stage_runs— dedicated performance-monitoring table: one row per(analysis, stage)trackingduration_ms/cost_usd/model, kept independent of the ecosystem-specific tables so it keeps working as more ecosystems are added.blast_radius_verdictsat read time to avoid keeping a denormalized copy in sync.CREATE TABLE/INDEX IF NOT EXISTS, noALTERon existing tables) — no lock risk onpackages/advisoriesin production.packages-dbimage (built fromscripts/packages-db/Dockerfile, which includespg_partman).Type of change
JIRA ticket
CM-1328